home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / macros / latex209 / contrib / chbars / chbars.tex (.txt) < prev    next >
LaTeX Document  |  1993-01-11  |  40KB  |  852 lines

  1. % This is CHBARS.TEX                      as of Aug '92
  2. % -*-TeX-*-
  3. %---------------------------------------------------------
  4. % (c) 1989 by J.Schrod. copy conditions see below.
  5. % Macro package for creating changebars in LaTeX.
  6. % MAKEPROG will ``weave'' this file into documentation that can be LaTeX'ed.
  7. % documented in LaTeX (for Anne and Chris)
  8. %       VERSION HISTORY  (MSCF -- most significant change first)
  9. % DATE     PERSON  REMARK
  10. % 92-08-15 -rlb    Use change bars in this document to mark major changes;
  11. %                  so now you can see for yourself what they are.
  12. % 92-07-28 -rlb    Run through a spelling checker before distributing.
  13. % 92-01-15 -rlb    1) Keep \maxdeadcycles the same; just 
  14. %                     don't count calls to output for change processing. 
  15. %                  2) Allow setting the change bar width.
  16. %                  3) Some typos and rewording of some of the comments.
  17. %                  4) Allow setting change bar width. Interface copied
  18. %                     from changebars.sty
  19. %                  5) Allow changebars to be on the left as well as
  20. %                     the right.
  21. %                  6) localize some variables global definitions now
  22. %                     start with cb_.
  23. %                  7) Chain onto existing \output routine rather than assume
  24. %                     \plainoutput.
  25. %                  And miscellaneous minor hacks.
  26. % 89-10-09 -js     converted to LaTeX (progltx)
  27. % 89-09-25 -js     repaired \mark processing in horizontal mode
  28. % 89-08    -js     first version (for EuroTeX89 in Karlsruhe)
  29. % author's current address:
  30. %       Detig$\,\cdot\,$Schrod \TeX{}sys
  31. %       Joachim Schrod
  32. %       Kranichweg 1
  33. %       D-6074 R\"odermark-Urberach
  34. %       FR Germany
  35. %       Tel. (+6074) 1617
  36. %       Bitnet: XITIJSCH@DDATHD21
  37. % should be progtex...
  38. %%%% These TeX macros were documented with the documentation system
  39. %%%% MAKEPROG and automatically converted to the current form.
  40. %%%% If you have MAKEPROG available you may transform it back to
  41. %%%% the original input: Remove every occurence of three percents
  42. %%%% and one optional blank from the beginning of a line and remove
  43. %%%% every line which starts with four percents.  The following lex
  44. %%%% program will do this:
  45. %%%%    %%
  46. %%%%    ^%%%\ ?   ;
  47. %%%%    ^%%%%.*\n ;
  48. %%%% MAKEPROG may be obtained over the net from the Bitnet-Listserver
  49. %%%% LISTSERV@DHDURZ1 (filelist WEBWARE), from tuglib@science.utah.edu,
  50. %%%% or via ftp from june.cs.washington.edu.
  51. %%% \documentstyle[progltx,chbars,a4-9]{article}
  52. %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  53. %%% %
  54. %%% % local macros
  55. %%% %
  56. %%% \let\mc=\small              % for names like GNU
  57. %%% \def\PS{{\sc PostScript}}
  58. %%% \def\DVI{{\tt DVI}}
  59. %%% \def\GNU{{\mc GNU}}
  60. %%% \chardef\bs=`\\
  61. %%% %
  62. %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  63. %%% \begin{document}
  64. %%% \tableofcontents
  65. %%% \title{Changebars without {\tt \bs{}special}'s}
  66. %%% \author{\sc Joachim Schrod}
  67. %%% \newcommand{\changedate}{Aug 15, 1992}
  68. %%% \date{Revised last on \changedate\footnote{by {\tt rocky@watson.ibm.com}}\\ 
  69. %%%       Formatted on \today}
  70. %%% \maketitle
  71. %%% \begin{abstract}
  72. %%% It is common practice to use vertical bars in the margins of a
  73. %%% document to mark pieces of text which have changed since the last
  74. %%% version(s) of this document. Such vertical bars are usually
  75. %%% called {\em changebars}. It has often been said that it is
  76. %%% impossible to produce changebars with \TeX{} without the usage of
  77. %%% |\special| commands (driver directives), which extend the
  78. %%% primitives of \TeX{}. This paper presents a \TeX{} macro file
  79. %%% which implements changebars without such a usage. The macro file
  80. %%% is written for the usage with {\sc Plain}~\TeX{} but the implementation 
  81. %%% strategy can be used with \LaTeX{}, too with minor changes.
  82. %%% \end{abstract}
  83. %%% \chap Introduction.
  84. %%% Changebars are used to mark modified parts in existing documents.  For
  85. %%% the usage in \TeX{} documents, there exist only solutions that use
  86. %%% driver/printer features by the way of inserting |\special| commands in
  87. %%% the \TeX{} source, e.g.\ for \PS{} drivers.  This results in documents
  88. %%% that are no longer as freely interchangeable as the \DVI{} concept would
  89. %%% allow---device dependency is problematic especially for this application
  90. %%% that is useful for multi-authoring or standards development.
  91. %%% This macro package offers a pure \TeX{} solution.  Nevertheless, it has
  92. %%% its restrictions, too. The page break will no longer be optimal,
  93. %%% because there is no strechability or shrinkability of a page 
  94. %%% on top of the last
  95. %%% region of change marked. But this seems to be acceptable,
  96. %%% especially as the change bar feature often will be used for proof
  97. %%% reading and not in the final document. This restriction is the reason
  98. %%% why no change marks can be used on title pages or on similar
  99. %%% constructions. Changes in floating insertions (footnotes, figures) are
  100. %%% not handled. Multi-column formats such as a two-column layout
  101. %%% will not work. There is currently no support for nested changes.
  102. %%% The method for writing a change bar consists of three
  103. %%% parts: First, the output routine is signalled when the beginning
  104. %%% or end of a change bar setting command is encountered.
  105. %%% Next, the position of the changed area is found out
  106. %%% and fixed; the end-of-change command adds the last change bar
  107. %%% position, length and width to a list of all such positions,
  108. %%% lengths and widths of change bars that is accumulated for the current 
  109. %%% page. Finally, when
  110. %%% the output routine is triggered (either asynchronously in trying
  111. %%% to ship out the page or synchronously when discovering that the
  112. %%% end of a change bar lies off the page), this list of change
  113. %%% bars information is used create vertical rules which are added to the page.
  114. %%% If the change bar is not complete, a ``virtual'' end change is
  115. %%% inserted, and a ``virtual'' begin change is inserted at the
  116. %%% beginning of the next page.
  117. %%% \beginchange
  118. %%% The demonstrated solution was originally written in {\sc Plain}
  119. %%% \TeX{}, because it was
  120. %%% easier and could be presented better at the Euro\TeX89 conference in
  121. %%% Karlsruhe.  An adaptation to \LaTeX{} has been done too which
  122. %%% requires minor modifications to the \LaTeX's output routine.
  123. %%% \endchange
  124. %%% Some history. The routines were initially written by Joachim
  125. %%% Schrod. Around Jan.\ 1992, R.\ Bernstein added some of the features
  126. %%% coded in |changebars.sty| to combine the best features
  127. %%% of the two (and added a couple of his own). For example, the
  128. %%% ability to specify change bar widths, put the change bars either on the
  129. %%% right or left margin, specifiy the distance from the margin to the
  130. %%% change bar, and chain to on top of a pre-existing modified 
  131. %%% output routine. 
  132. %%% \beginchange
  133. %%% Initially, both the {\TeX} and the {\LaTeX}
  134. %%% versions were put into one file. However, due problems in
  135. %%% dealing with conditional definition of code, in particular
  136. %%% problems with an extra or omitted |\fi| in defining or not
  137. %%% |\ifr@ggedbottom|, the code was split into two. 
  138. %%% \endchange
  139. %%% The |changebars.sty| package
  140. %%% was written by Michael Fine and revised by Johannes Braams
  141. %%% and Neil Winton. One or two ideas from Thomas J.~Reid have been
  142. %%% used.
  143. %%% \chap GNU General Public License.
  144. %%% This program is free software; you can redistribute it and/or
  145. %%% modify it under the terms of the \GNU{} General Public License as
  146. %%% published by the Free Software Foundation; either version~2, or (at your
  147. %%% option) any later version.
  148. %%% This program is distributed in the hope that it will be useful, but
  149. %%% {\bf without any warranty\/}; without even the implied warranty of
  150. %%% {\bf merchantability\/} or {\bf fitness for a particular purpose}.  See
  151. %%% the \GNU{} General Public License for more details.
  152. %%% You should have received a copy of the \GNU{} General Public License
  153. %%% along with this program; if not, write to the Free Software Foundation,
  154. %%% Inc., 675~Mass Ave, Cambridge, MA~02139, USA.
  155. %%% \chap User Interface.
  156. %%% \begin{newsloppy}
  157. %%% A changed area is described by two marks, |\beginchange| and
  158. %%% |\endchange|. The |\beginchange| can take an optional argument,
  159. %%% the bar thickness, enclosed in brackets. That is, either of the
  160. %%% something like |\beginchange[0.4pt]|, or |\beginchange| is valid. 
  161. %%% In the latter case where no thickness dimension is specified, the value
  162. %%% of |\changebarwidth| is used. Therefore, by setting this you can
  163. %%% change the bar thickness used when none is given. Changing the bar
  164. %%% thickness might be useful in identifying different classes of changes.
  165. %%% Another pair of global values that can be set are the 
  166. %%% logical variables 
  167. %%% {\tt \bs chbar\-Right\-true} and {\tt \bs chbar\-Right\-false}. 
  168. %%% These specify whether
  169. %%% changebars should be on the right or left. Say, if one wanted to
  170. %%% have the changebars always appear on the outside margin, one could
  171. %%% change these accordingly on each page.
  172. %%% \end{newsloppy}
  173. %%% Finally |\BarDistance| is the amount of place between the text margin
  174. %%% and the change bars. This value should normally be positive. For
  175. %%% change bars on the right this value is added to |\hsize|, while
  176. %%% for change bars on the left this value is {\em subtracted\/} from
  177. %%% |\hoffset|.
  178. %%% \beginprog
  179. \newdimen\changebarwidth \changebarwidth=1pt\relax
  180. \newif\ifchbarRight \chbarRightfalse
  181. \newdimen\BarDistance  \BarDistance=2cc
  182. %%% \endprog
  183. %%% \chap Utility Routines and Programming Conventions.
  184. %%% Before we get into the nitty gritty details, we give some common
  185. %%% macros. 
  186. %%% First, we declare some shorthands for category codes.  By
  187. %%% declaring the at sign~(`|@|') as well as the underscore~`(|_|)' as
  188. %%% letters we can use them in our macros. (I agree with D.~Knuth that
  189. %%% |\identifier_several_words_long| is more readable than
  190. %%% |\IdentifierSeveralWordsLong| and in every case better than |\p@@@s|.)
  191. %%% \beginchange
  192. %%% By defining the at sign to be in the letter class,
  193. %%% we can access {\sc Plain} \TeX's ``private''macros. By defining the
  194. %%% underscore to be in the letter class, we make our own private macros more 
  195. %%% readable. But since have to
  196. %%% restore these category codes at the end of this macro file, we store
  197. %%% \endchange
  198. %%% their former values in the control sequences |\atcode| and |\uscode|.
  199. %%% This method is better than to use a group because not all macros have to
  200. %%% be defined global this way.
  201. %%% All {\em global\/} definitions of the package are prefixed by
  202. %%% |cb_|. In this way, they can be easily determined. If other
  203. %%% packages do likewise, it is less likely that definition
  204. %%% names will clash between packages. (Unless the other package uses the
  205. %%% |cb_| prefix too, in which case there is a good chance that many
  206. %%% variable names will clash.)
  207. %%% The {\TeX}book recommends the 255 registers be used for scratch
  208. %%% space, so routines use this value when a spare register is
  209. %%% needed. (See for example |\cb_write_bar|.)
  210. %%% \beginprog
  211. \chardef\letter=11
  212. \chardef\atcode=\catcode`\@
  213. \chardef\uscode=\catcode`\_
  214. \catcode`\@=\letter
  215. \catcode`\_=\letter
  216. %%% \endprog
  217. %%% \chap Utility Routines for \TeX vs.\ \LaTeX
  218. %%% If this program was not run from {\LaTeX}, \@ifundefined
  219. %%% may be, well, undefined. So, we define it. What do you know---the
  220. %%% the code for the macro almost contains a copy if itself? 
  221. %%% Also, if this is run from {\sc Plain} {\TeX}, there are a few other macros
  222. %%% we need to copy in. These we borrow (sort of) from {\LaTeX},
  223. %%% if they were undefined. In particular, they are:
  224. %%% |\@ifundefined{NAME}{YES}{NO}|: if |\NAME| is undefined then it
  225. %%% executes |YES|, otherwise it executes NO. More precisely,
  226. %%% true if |\NAME| either undefined or = |\relax|.
  227. %%% |\@ifnextchar X{YES}{NO}|: Expands to |YES| if next character is
  228. %%% an 'X', and to |NO| otherwise. (Uses temps a-c). {\it Note:
  229. %%% gobbles any space following it.}
  230. %%% \beginprog
  231. \expandafter\ifx\csname @ifundefined\endcsname\relax%
  232.   \long\def\@ifundefined#1#2#3{\expandafter\ifx\csname 
  233.   #1\endcsname\relax#2\else#3\fi}%
  234.   \fi
  235. \def\typeout#1{{\immediate\write16{#1}}}
  236. % \@ifnextchar X{YES}{NO} 
  237. %  BEGIN
  238. %    \@tempe := X  % uses \let
  239. %    \@tempa := YES
  240. %    \@tempb := NO
  241. %    \futurelet\@tempc
  242. %    \@ifnch
  243. %  END
  244. % \@ifnch ==
  245. %   BEGIN
  246. %     if  \@tempc = blank space
  247. %       then  \@tempd := def(\@xifnch)
  248. %       else  if  \@tempc = \@tempe
  249. %                then  \@tempd := def(\@tempa)
  250. %                else  \@tempd := def(\@tempb)
  251. %             fi
  252. %     fi
  253. %     \@tempd
  254. %   END
  255. % \@xifnch ==
  256. %  BEGIN
  257. %    gobble blanks
  258. %    \futurelet\@tempc
  259. %    \@ifnch
  260. %  END
  261. \def\@ifnextchar#1#2#3{\let\@tempe #1\def\@tempa{#2}\def\@tempb{#3}\futurelet
  262.   \@tempc\@ifnch}%
  263. \def\@ifnch{\ifx \@tempc \@sptoken \let\@tempd\@xifnch%
  264.     \else \ifx \@tempc \@tempe\let\@tempd\@tempa\else\let\@tempd\@tempb\fi
  265.     \fi \@tempd}
  266. % NOTE: the following hacking must precede the definition of \:
  267. %  as math medium space.
  268. \def\:{\let\@sptoken= } \:  % this makes \@sptoken a space token 
  269. \def\:{\@xifnch} \expandafter\def\: {\futurelet\@tempc\@ifnch}
  270. %%% \endprog
  271. %%% \sect Now we are ready to code the top-level routine for indicating the 
  272. %%% beginning of a change. But first we display the banner and
  273. %%% version number associated with this package. If this package has
  274. %%% been loaded already we terminate.
  275. %%% Although one can specify a bar
  276. %%% thickness on the |\beginchange| macro, it is at the |\endchange|
  277. %%% where this thickness is recorded and put on a current list of
  278. %%% change bar entries for the current page. It seems more natural to 
  279. %%% specify the thickness at the beginning of the change rather than
  280. %%% the end. Therefore at the
  281. %%% |\beginchange| we merely save the value given or save the value of
  282. %%% |\changebarwidth| if no thickness was given.
  283. %%% |\cb_changebarwidth| is this saved value. Auxiliary routine
  284. %%% |\cb_xstart| is used to parse off the optional enclosing
  285. %%% brackets. (This weird name was taken from one used in |changebars.sty|.)
  286. %%% default value, that is the value when a width is not specified 
  287. %%% is |\changebarwidth|.
  288. %%% \beginprog
  289. \@ifundefined{cb_xstart}{}{\endinput}
  290. \typeout{Style option `change bars' -- version 1.1a}
  291. \newdimen\cb_changebarwidth
  292. \def\beginchange{\@ifnextchar [
  293.                     {\cb_xstart}
  294.                     {\beginchange_\changebarwidth}}
  295. \def\cb_xstart[#1]{\beginchange_{#1}}
  296. %%% \endprog
  297. %%% \chap Triggering the Output Routine.
  298. %%% \beginchange
  299. %%% When a change bar is started or completed, we signal the output
  300. %%% routine in the ``usual'' way described below. Why bother the
  301. %%% output routine? The output routine is supposed to be thought of 
  302. %%% as an asynchronous process which has access to the page via the
  303. %%% contents of box register 255. In theory, if we did not perform
  304. %%% actions in the output routine, it might get triggered inadvertantly,
  305. %%% and the output routine might modify this box register on us in the
  306. %%% midst of our work. The information that needs to be recorded
  307. %%% is the position of the beginning and end of the lines containing a
  308. %%% marked change region.
  309. %%% \begin{newsloppy}
  310. %%% The macros |\beginchange| and |\endchange| signal the output
  311. %%% routine. Rather than merely calling the output routine directly,
  312. %%% these routines signal the output routine by setting a low page penalty, i.e.,
  313. %%% one which tells {\TeX} that this is a really good place to break
  314. %%% the page. Of course, we will modify the output routine so that it
  315. %%% doesn't really split the page when it has been called in this fashion.
  316. %%% Again, the reason we signal the output routine in what seems at
  317. %%% first a pretty odd way, is that this is the way it is supposed to
  318. %%% be done. If a low penalty were not set, some 
  319. %%% other action (if we weren't careful) might cause a penalty to be 
  320. %%% set and thus call the output routine (recursively).
  321. %%% So that we can distinguish our calls from others, |\beginchange|
  322. %%% and |\endchange| reserve a range of 
  323. %%% penalty values; he actual value is stored in |\cb_break_penalty|.
  324. %%% This range of values is below 
  325. %%% $-10\,000$, the nominal value for indicating that a page should
  326. %%% occur now. The penalty will be in the range $|\cb_penalty_group|
  327. %%% \cdot 100 - 99 \ldots |\cb_penalty_group| \cdot 100$.
  328. %%% The output routine then decides whether it has been called 
  329. %%% to start or end a change bar or neither.  This is done with the values
  330. %%% |\cb_penalty_begin| and |\cb_penalty_end| that are used as the
  331. %%% second-to-last digit of the change penalty.
  332. %%% \end{newsloppy}
  333. %%% The output routine also needs to determine whether
  334. %%% the beginning of change bar starts in horizontal or vertical mode
  335. %%% so it can figure out the exact placement for the beginning of the
  336. %%% bar line. In horizontal mode, the change bar does
  337. %%% not begin at the baseline of the actual text position, but on top of the
  338. %%% actual line.  This is marked in the last digit, an odd digit will be
  339. %%% used in horizontal mode.
  340. %%% Note that the values mentioned above are used as digits here that can be
  341. %%% concatenated.  If they are not followed by an other digit they should be
  342. %%% terminated by |\space| to stop {\TeX}'s look-ahead scanning for
  343. %%% digits when reading a number. 
  344. %%% \endchange
  345. %%% \beginprog
  346. \def\cb_penalty_group{-101}
  347. \def\cb_penalty_begin{0}
  348. \def\cb_penalty_end{1}
  349. %%% \endprog
  350. %%% \sect The calls to start or end a change bar, set an encoded page-break
  351. %%% penalty which includes an
  352. %%% indication of begin or end of a change bar. The variable
  353. %%% |\cb_break_penalty| is used to create such as special value.
  354. %%% The rest (|\cb_trigger_output|) is the same action
  355. %%% for both. The region just before the end of the changed region can be 
  356. %%% in horizontal mode and preceded by
  357. %%% glue that could cause a line break, thus including the following line to
  358. %%% the change area as well.  To avoid this unwanted behavior, the space is
  359. %%% saved in |\save_lastskip|, discarded in front of the mark and restored
  360. %%% afterwards. 
  361. %%% \beginprog
  362. \newcount\cb_break_penalty
  363. \def\beginchange_#1{%
  364.    \cb_changebarwidth=#1
  365.    \cb_break_penalty=\cb_penalty_group\cb_penalty_begin0
  366.    \cb_trigger_output
  367. \def\endchange{{%
  368.    \skipdef\save_lastskip=255%
  369.    \ifhmode \save_lastskip=\lastskip \unskip \fi%
  370.    \cb_break_penalty=\cb_penalty_group\cb_penalty_end0%
  371.    \cb_trigger_output%
  372.    \ifhmode \hskip\save_lastskip \fi%
  373.    }}%\endchange
  374. %%% \endprog
  375. %%% \sect The next routine, |\cb_trigger_output|,
  376. %%% triggers calls to the output routine by setting the page-break 
  377. %%% penalty, |\penalty|. {\TeX} may discard an |\output| invocation at
  378. %%% the beginning of a page.  So we trigger the output routine twice, first
  379. %%% with a special penalty value that is 2~less than the correct value
  380. %%% (including the code for horizontal or vertical mode).  After the first
  381. %%% page break, it is asserted that the current list is empty.  The output
  382. %%% routine has to save the former page contents if necessary.
  383. %%% Next we set the penalty to the correct value.  The second page break does
  384. %%% the real work, restores the page contents and handles the split
  385. %%% insertions (footnotes, figures,~\dots). 
  386. %%% In horizontal mode |\spacefactor| must not be destroyed, so it is
  387. %%% saved and restored via local count register |\save_spacefactor|.
  388. %%% \beginprog
  389. \def\cb_trigger_output{%
  390.    \ifinner \errmessage{Change cannot be marked inside a box}%
  391.    \else{%
  392.      \countdef\save_spacefactor=255%
  393.      \ifvmode   
  394.        \let\do_in_vmode=\relax
  395.        \advance \cb_break_penalty by -2
  396.      \else 
  397.        \save_spacefactor=\spacefactor
  398.        \let\do_in_vmode=\vadjust
  399.        \advance \cb_break_penalty by -3
  400.      \fi
  401.      \do_in_vmode{%
  402.        \penalty\cb_break_penalty%        first call to \output
  403.        \null
  404.        \advance \cb_break_penalty by 2 
  405.        \penalty\cb_break_penalty%        second call to \output
  406.        }%
  407.      \ifhmode \spacefactor=\save_spacefactor \fi
  408.      }%
  409.    \fi
  410.    }% cb_trigger_output
  411. %%% \endprog
  412. %%% \sect Using the output routine for passing
  413. %%% information has it's difficulties.  One of the hard parts is 
  414. %%% \beginchange
  415. %%% handling page marks. These are token lists which are set by the
  416. %%% |\mark| command. They record information that can later 
  417. %%% be accessed by the output routine. The canonical example of such
  418. %%% a use of the |\mark| command is in creating 
  419. %%% \endchange
  420. %%% dictionary-style entry headings.
  421. %%% The output routine can access one of three
  422. %%% token lists through three control sequences:  |\botmark| is the
  423. %%% last page mark given, |\topmark| is the |\botmark| of the previous
  424. %%% page, and |\firstmark| is the first page mark on the actual page or
  425. %%% |\topmark| if none was given.  Here ``page'' is used in the \TeX{}
  426. %%% sense, i.e.\ as the material which has been collected between two
  427. %%% |\output| invocations. Of course, the page marks must not be
  428. %%% destroyed---and that means they must be reinserted after each
  429. %%% special use of the output routine.
  430. %%% But we are lucky: A ``special use'' consists of two |\output|
  431. %%% invocations, so we can insert |\topmark| again as a page mark after
  432. %%% the first invocation where it will be the only page mark on that
  433. %%% \TeX{} page.  The second invocation will automatically transform this
  434. %%% page mark into the ``last page mark on the previous page,'' i.e.\ in
  435. %%% |\topmark|---that's what we need!  Furthermore |\firstmark| and
  436. %%% |\botmark| are saved in control sequences during the first invocation,
  437. %%% they will be inserted again, too.
  438. %%% There's one situation where this approach doesn't work: in front of
  439. %%% the first page mark. Here, |\topmark|, |\firstmark|, and |\botmark| expand
  440. %%% to an empty token list. If we save them then and insert their old
  441. %%% values we have inserted empty page marks. If other page marks follow
  442. %%% on the same ``real'' page, |\firstmark| will be empty instead of
  443. %%% expanding to the token list of the first page mark.  To prevent this
  444. %%% we must not save and restore page marks before the first |\mark| has
  445. %%% been added to the main vertical list.
  446. %%% Well, that can be controlled with a switch---but this switch must be
  447. %%% set very carefully.  If it is set immediately by the first |\mark|
  448. %%% this may be in horizontal mode and special output invocations can
  449. %%% occur above this page mark (i.e., there may be a |\beginchange| in the
  450. %%% same paragraph in front of the |\mark|).  Therefore the setting of the
  451. %%% switch must be delayed until the vertical position of the |\mark|
  452. %%% (precisely:  the position of the |\mark| in the current list) is
  453. %%% reached.  In horizontal mode this can be done with a |\vadjust| and
  454. %%% the output routine! Voil\`a, this is another command group for the
  455. %%% output routine with only one command.
  456. %%% \beginprog
  457. \newif\if_cb_save_mark@  \_cb_save_mark@false
  458. \def\mark_penalty_group{-102}
  459. %%% \endprog
  460. %%% \sect We will redefine |\mark| so that the first page mark either sets
  461. %%% the switch to true (in vertical mode all possible special page breaks
  462. %%% are already handled) or forces the |\output| routine to do this at an
  463. %%% appropriate place. In the last case we can use |\cb_trigger_output|
  464. %%% again. Afterwards we restore the original meaning of |\mark| again to
  465. %%% reduce the processing overhead (and the dead cycles).
  466. %%% This change of |\mark| has the consequence that the first |\mark| in a
  467. %%% document cannot be used anymore in horizontal mode inside a vertical
  468. %%% box that shall be split afterwards.  But this is only sensible if this
  469. %%% mark shall be used as |\splitfirstmark| because it will almost never
  470. %%% migrate to the outer list---really a rare case!
  471. %%% \beginprog
  472. \let\cb_mark=\mark
  473. \def\mark{%
  474.    \ifvmode
  475.      \ifinner \else  \global\_cb_save_mark@true \fi % split marks!
  476.    \else  
  477.      \cb_break_penalty=\mark_penalty_group00 % this will corrupt \vsplit
  478.      \cb_trigger_output
  479.    \fi
  480.    \global\let\mark=\cb_mark
  481.    \cb_mark
  482. %%% \endprog
  483. %%% \sect 
  484. %%% \begin{newsloppy}
  485. %%% If the output routine is triggered with the mark penalty value,
  486. %%% it will call {\tt \bs cb\_save\discretionary{\_}{}{}page\_marks}.
  487. %%% \end{newsloppy}
  488. %%% \beginprog
  489. \def\cb_save_page_marks{%            % this may be executed twice
  490.    \unvbox\@cclv
  491.    \global\_cb_save_mark@true
  492. %%% \endprog
  493. %%% \sect To finish the treatment of page marks we can formulate the two
  494. %%% macros which are used at the first resp.\ second invocation of a
  495. %%% ``special output,'' the principles have already been explained.
  496. %%% \beginprog
  497. \def\cb_backup_page_marks{%
  498.    \if_cb_save_mark@
  499.       \mark{\topmark}%
  500.       \xdef\cb_save_firstmark{\firstmark}%
  501.       \xdef\cb_save_botmark{\botmark}%
  502.    \fi
  503. \def\cb_restore_page_marks{%
  504.    \if_cb_save_mark@
  505.       \mark{\cb_save_firstmark}\mark{\cb_save_botmark}%
  506.    \fi
  507. %%% \endprog
  508. %%% \chap Positioning the Change Bars.
  509. %%% \begin{newsloppy}
  510. %%% Now we handle the positions of the bars.  
  511. %%% The dimension |\cb_bot_change_pos| will hold
  512. %%% the position of the end of the change bar, i.e.\ the distance between 
  513. %%% top of page the end of the changed area. The dimension
  514. %%% |\cb_top_change_pos| will hold the beginning of a changed
  515. %%% area; a value of |\maxdimen| indicates that no change is in effect.  If
  516. %%% a changed area is completed, it is appended to the list |\cb_bar_list| as
  517. %%% an element {\tt \bs cb\_bar(\bs cb\_top\_change\_pos,
  518. %%% \bs cb\_bot\_change\_pos, \bs changebar\-width)}.  This list contains all
  519. %%% changed areas within the current page so that bars can be written later
  520. %%% on.  A single bar will be produced by |\cb_write_bar|.
  521. %%% \noindent The definition of |\cb_bar| to |\relax| allows the concatenation
  522. %%% of new elements to |\cb_bar_list| with |\xdef|.
  523. %%% Local dimension register |\halfwidth| is used to center the change bar.
  524. %%% \end{newsloppy}
  525. %%% \beginprog
  526. \newdimen\cb_bot_change_pos
  527. \newdimen\cb_top_change_pos  \cb_top_change_pos=\maxdimen
  528. \let\cb_bar_list=\empty
  529. \let\cb_bar=\relax
  530. \def\cb_write_bar(#1,#2,#3){{%
  531.    \dimendef\halfwidth=255%
  532.    \setbox0=\hbox{\vrule width #3 height -#1  depth #2}%
  533.    \dp0=0pt \ht0=0pt \wd0=0pt%
  534.    \halfwidth=#3 \divide\dimen255 by 2%
  535.    \hskip -\halfwidth%
  536.    \box0%
  537.    \hskip \halfwidth%
  538.    }}
  539. %%% \endprog
  540. %%% \sect If the output routine was activated by a |\outputpenalty| value
  541. %%% within the range of our reserved penalties, the change handling will
  542. %%% occur, otherwise standard plain output can be done. There may be
  543. %%% a lot of interaction between the routines which set change bars and
  544. %%% the output routine. These interactions should not be recorded in
  545. %%% |\deadcycles| or else \TeX{} will soon grumble. One might consider
  546. %%% doing the same for the|\mark_penalty_group|. But since this 
  547. %%% group is called once it shouldn't matter all that much.
  548. %%% One might also consider adding a counter like |\deadcycles| just
  549. %%% to count the change bar interactions as is done for the calls to
  550. %%% |\output|, and have {\TeX} grumble if there are ``too many'' of
  551. %%% them. For now we don't do this---all of this code is correct
  552. %%% anyway!
  553. %%% First we save away the old output routine in case the user had redefined
  554. %%% this beforehand.
  555. %%% \beginprog
  556. \newtoks\cb_oldoutput
  557. \edef\cb_oldoutput{\the\output}
  558. \newcount\penalty_group
  559. \output={%
  560.    \boxmaxdepth=\maxdepth
  561.    \penalty_group=\outputpenalty  \divide \penalty_group by 100
  562.    \ifnum \penalty_group=\cb_penalty_group\space  
  563.      {\countdef\new_deadcycles=255% don't count as a dead cycle
  564.       \new_deadcycles=\deadcycles       
  565.       \advance \new_deadcycles by -1
  566.       \deadcycles=\new_deadcycles}
  567.      \cb_change_handling
  568.    \else
  569.       \ifnum \penalty_group=\mark_penalty_group\space
  570.          \cb_save_page_marks
  571.       \else \cb_oldoutput
  572.       \fi
  573.    \fi
  574. %%% \endprog
  575. %%% \sect As explained before, the change handling must differentiate
  576. %%% between the kind of the change command (beginning is indicated by
  577. %%% $|\cb_change_cmd|=0$, end by~1) and between the mode (horizontal indicated
  578. %%% by an odd |\cb_change_mode| value, vertical by an even).  A change mode
  579. %%% higher than one indicates that we are doing the first page break that
  580. %%% has to backup the page as far as it exists already and results in an
  581. %%% empty current list of page elements.
  582. %%% \beginprog
  583. \newcount\cb_change_cmd
  584. \newcount\cb_change_mode
  585. \def\cb_change_handling{%
  586.    \cb_change_cmd=-\outputpenalty                  % ==> absolute value
  587.    \advance \cb_change_cmd by \cb_penalty_group00  % subtraction
  588.    \cb_change_mode=\cb_change_cmd
  589.    \divide \cb_change_cmd by 10                    % second-to-last digit
  590.    \advance \cb_change_mode by -\number\cb_change_cmd0   % last digit
  591.    \ifnum \cb_change_mode>1  \cb_backup_page
  592.    \else
  593.       \ifcase \cb_change_cmd  \cb_begin_change
  594.       \or \cb_end_change
  595.       \else \errmessage{Invalid changepenalty}%
  596.       \fi
  597.    \fi
  598. %%% \endprog
  599. %%% \sect Processing a mark during the second trigger of the output routine
  600. %%% means restoring the page and storing the positions.  At the beginning,
  601. %%% the begin of the change is saved, at the end, we know the bar already
  602. %%% and put it into the bar list.  Then the positioning values are
  603. %%% reinitialized.
  604. %%% As within every output invocation, the box 255 must be unboxed.  As we
  605. %%% are here in the second invocation of the output routine the |\box255|
  606. %%% consists only of the empty |\vbox| we have inserted in
  607. %%% |\cb_trigger_output|.  We can therefore throw it away.
  608. %%% \beginprog
  609. \def\cb_begin_change{%
  610.    \cb_restore_page
  611.    \setbox0=\box\@cclv
  612.    \ifdim \cb_top_change_pos=\maxdimen
  613.       \global\cb_top_change_pos=\cb_bot_change_pos  
  614.       \global\cb_bot_change_pos=0pt
  615.    \else \errmessage{Nested change bars are not supported}%
  616.    \fi
  617. \def\cb_end_change{%
  618.    \cb_restore_page
  619.    \setbox0=\box\@cclv
  620.    \ifdim \cb_top_change_pos=\maxdimen
  621.       \errmessage{No change is in effect}%
  622.    \else
  623.       \xdef\cb_bar_list{\cb_bar_list 
  624.          \cb_bar(\the\cb_top_change_pos,\the\cb_bot_change_pos,
  625.                  \the\cb_changebarwidth)}
  626.       \global\cb_top_change_pos=\maxdimen
  627.       \global\cb_bot_change_pos=0pt
  628.    \fi
  629. %%% \endprog
  630. %%% \chap Handling the Page Contents.
  631. %%% We handle the part of the page that was collected up to now by putting
  632. %%% it into a box. This fixes the position of the change mark so that
  633. %%% |\cb_bot_change_pos| can be set and stored in |\cb_top_change_pos|
  634. %%% later on or as the lower end of a bar in |\cb_bar_list|.
  635. %%% In the first output invocation, we save the contents of the 
  636. %%% page in |\cb_save_page|. Before that, we store the size of the
  637. %%% box (which equals |\pagegoal|!)\ in |\cb_page_goal|.  If the unboxing
  638. %%% caused an increase of height (i.e.\ if $|\pagetotal|>|\pagegoal|$),
  639. %%% we eject the page up to the change mark.  Now we have to compute the
  640. %%% current position of our mark in |\cb_bot_change_pos|. It is fixed by the size
  641. %%% of the |\cb_save_page|, but if the change begins in horizontal mode we
  642. %%% must decrease it from the baseline position to the top of the last line.
  643. %%% Finally, we must save the values for the allowed insertions and change
  644. %%% them to the maximal value so that a rest that is split from an insertion
  645. %%% will be appended to the insertion box at the second invocation in every
  646. %%% case.
  647. %%% The |\vsize| is initialized to |\maxdimen|.  This allows to control
  648. %%% whether this first output invocation ocurred or if it was discarded.
  649. %%% For the same reason |\cb_bot_change_pos| is initialized to~0pt.
  650. %%% \beginprog
  651. \newbox\cb_save_page
  652. \newdimen\cb_page_goal
  653. \newdimen\cb_save_vsize         \cb_save_vsize=\maxdimen
  654. \newdimen\cb_save_dimen_topins
  655. \newdimen\cb_save_dimen_footins
  656. \def\cb_backup_page{%
  657.    \global\cb_page_goal=\ht\@cclv
  658.    \global\setbox\cb_save_page=\vbox{\unvbox\@cclv}%
  659.    \ifdim \ht\cb_save_page>\cb_page_goal  
  660.       \cb_eject_page_so_far \fi
  661.    \cb_bot_change_pos=\ht\cb_save_page  
  662.    \global\advance \cb_bot_change_pos by \dp\cb_save_page
  663.    \ifnum \cb_change_cmd=\cb_penalty_begin\space
  664.       \ifodd \cb_change_mode \higher_change_pos \fi
  665.    \fi
  666.    \global\cb_save_vsize=\vsize  \global\vsize=\maxdimen
  667.    \global\cb_save_dimen_topins=\dimen\topins  
  668.    \global\dimen\topins=\maxdimen
  669.    \global\cb_save_dimen_footins=\dimen\footins
  670.    \global\dimen\footins=\maxdimen
  671.    \cb_backup_page_marks
  672. \cb_bot_change_pos=0pt
  673. %%% \endprog
  674. %%% \sect To eject a page as far as it is we restore it from the
  675. %%% |\cb_save_page| back to box~255.  In horizontal mode and at a begin mark
  676. %%% the last line contains the mark and must not be output.  So we remove it
  677. %%% and the preceding glue from the stored rest, just leaving a single hbox
  678. %%% to be on top of the actual page (in |\cb_save_page|) now. Then normal
  679. %%% output can be done with box~255.
  680. %%% \beginprog
  681. \def\cb_eject_page_so_far{%
  682.    \begingroup
  683.      \vbadness=20000 % don't complain about underfull vboxes
  684.      \global\setbox\@cclv=\vbox to \cb_page_goal{%
  685.          \unvbox\cb_save_page
  686.          \ifnum \cb_change_cmd=\cb_penalty_begin\space
  687.             \ifodd \cb_change_mode
  688.                \global\setbox\cb_save_page=\lastbox
  689.                \unskip
  690.             \fi
  691.          \fi
  692.          }%
  693.    \endgroup
  694.    \cb_oldoutput
  695. %%% \endprog
  696. %%% \sect 
  697. %%% \begin{newsloppy}
  698. %%% In horizontal mode and at a begin mark, we need the position of
  699. %%% the mark ({\tt \bs cb\_bot\discretionary{\_}{}{}change\_pos}) on the upper boundary of 
  700. %%% the last line in
  701. %%% |\cb_save_page|.  If there is just one line left from a recent eject, the
  702. %%% height is given by |\topskip| decreased by the height of this hbox.  If
  703. %%% the height of the box is larger than |\topskip| the skip will not be
  704. %%% inserted and the change position results to~0pt. Otherwise,
  705. %%% |\cb_save_page| is a vbox whose last hbox we delete temporarily using
  706. %%% box~0. Height and depth of the rest are the actual position on the page.
  707. %%% \end{newsloppy}
  708. %%% The double of the page we have constructed this way will immediately be
  709. %%% fed back to the garbage collector because it could have become
  710. %%% reasonably large.
  711. %%% \beginprog
  712. \def\higher_change_pos{%
  713.    \ifhbox \cb_save_page % rest of page from \cb_eject_page_so_far
  714.       \cb_bot_change_pos=\topskip  
  715.       \global\advance \cb_bot_change_pos by -\ht\cb_save_page
  716.       \ifdim \cb_bot_change_pos<0pt  
  717.          \global\cb_bot_change_pos=0pt  
  718.       \fi
  719.    \else
  720.       \setbox0=\vbox{%
  721.          \unvcopy\cb_save_page
  722.          \setbox0=\lastbox % delete last line
  723.          }%
  724.       \cb_bot_change_pos=\ht0   
  725.       \global\advance \cb_bot_change_pos by \dp0
  726.       \setbox0=\box\voidb@x
  727.    \fi
  728. %%% \endprog
  729. %%% \sect To restore a page during the second output invocation, we first
  730. %%% restore the saved values, but only if they were really changed (this can
  731. %%% be discovered by the value of |\cb_save_vsize|).  Now the |\cb_save_page| is
  732. %%% appended to the current list as a box, which stops later usage of its
  733. %%% stretch- and shrinkability!  Then the collected insertions can be
  734. %%% inserted again. The page marks have to be inserted, too.
  735. %%% \beginprog
  736. \def\cb_restore_page{%
  737.    \ifdim \cb_save_vsize=\maxdimen
  738.    \else  \global\vsize=\cb_save_vsize
  739.      \global\dimen\topins=\cb_save_dimen_topins
  740.      \global\dimen\footins=\cb_save_dimen_footins
  741.      \global\cb_save_vsize=\maxdimen
  742.      \cb_restore_page_marks
  743.    \fi
  744.    \box\cb_save_page % discards stretch- and shrinkability!
  745.    \ifvoid \topins
  746.    \else  \insert\topins{\floatingpenalty=0 \unvbox\topins}%
  747.    \fi
  748.    \ifvoid \footins
  749.    \else  \insert\footins{\floatingpenalty=20000 \unvbox\footins}%
  750.    \fi
  751. %%% \endprog
  752. %%% \sect {\em Please note, that there is still a problem with this concept 
  753. %%% of handling the output trigger:}
  754. %%% \bigskip
  755. %%% If the first output trigger is discarded because a page break has occurred 
  756. %%% just in front, footnote parts may be juggled around. I.e., if a 
  757. %%% footnote is split in three parts, the first part was just been shipped 
  758. %%% out, the second part is inserted back into the recent contributions by 
  759. %%% the output routine but {\em behind\/} the third part which is saved in 
  760. %%% the ``special place'' (according to the \TeX{}book, p.~125). A solution to
  761. %%% this problem might be to insert a |\do_change| again within the second
  762. %%% output trigger and finishing the treatment afterwards. Afterwards a 
  763. %%% full triggering process (two output invocations) is executed again and 
  764. %%% all insertion parts will be accessible in the insertion box.
  765. %%% By the way, the almost same problem appears in \LaTeX{}, too. Almost: in
  766. %%% \LaTeX{} this can happen every time because at the first output invocation
  767. %%% the |\dimen|-values of the footnote insertion is not increased. I leave 
  768. %%% the problem open to the reader\,\dots
  769. %%% \chap Writing the Stuff.
  770. %%% The positions of the bars which mark the changed areas are relative to
  771. %%% the top of the text, i.e.\ the height of the top insertion is not
  772. %%% included.  Therefore it is best to write them just after the top
  773. %%% insertions before the page text---but to do this we have to change the
  774. %%% \beginchange
  775. %%% either the {\sc Plain} {\TeX} macro |\pagecontents| or the {\LaTeX}
  776. %%% macro |\@makecol|.
  777. %%% \endchange
  778. %%% Below is the new definition, I have just rearranged it a little bit so
  779. %%% that it is more legible.  The new lines have been marked with
  780. %%% `|%%%%|'.  |\cb_insert_current_bar| inserts a last element in |\cb_bar_list|
  781. %%% if a changed area is not yet finished, afterwards all bars can be
  782. %%% written.
  783. %%% \beginprog
  784. \def\pagecontents{%
  785.   \ifvoid \topins \else \unvbox\topins \fi
  786.   \cb_insert_current_bar \cb_write_all_bars             %%%%
  787.   \dimen@=\dp\@cclv \unvbox\@cclv % open up \box255
  788.   \ifvoid \footins
  789.   \else % footnote info is present
  790.      \vskip\skip\footins
  791.      \footnoterule
  792.      \unvbox\footins
  793.   \fi
  794.   \ifr@ggedbottom \kern-\dimen@ \vfil \fi
  795. %%% \endprog
  796. %%% \sect If $|\cb_top_change_pos|=|\maxdimen|$ no change is active.  Otherwise
  797. %%% the current change reaches from the begin mark (|\cb_top_change_pos|) to
  798. %%% the end of the page, i.e.\ we insert a virtual end mark.  Because the
  799. %%% change continues on the next page we insert a virtual begin mark on the
  800. %%% top of the page, too.
  801. %%% \beginprog
  802. \def\cb_insert_current_bar{%
  803.    \ifdim \cb_top_change_pos=\maxdimen
  804.    \else%
  805.       \cb_bot_change_pos=\ht\@cclv
  806.       \advance\cb_bot_change_pos by \dp\@cclv
  807.       \xdef\cb_bar_list{\cb_bar_list 
  808.                           \cb_bar(\the\cb_top_change_pos,
  809.                                   \the\cb_bot_change_pos,
  810.                                   \the\changebarwidth)}%
  811.       \global\cb_top_change_pos=0pt
  812.    \fi%
  813. %%% \endprog
  814. %%% \sect Now we can write all bars---if they exist anyway.  It's rather
  815. %%% easy, we just have to define |\cb_bar| to |\cb_write_bar| and execute
  816. %%% |\cb_bar_list|.  The resulting output must not use vertical place.  We must
  817. %%% not forget to delete the list, or we will get the same bars on the next
  818. %%% page again.
  819. %%% \beginprog
  820. \newbox\cb_bars
  821. \newdimen\cb_offset
  822. \def\cb_write_all_bars{%
  823.    \ifx \cb_bar_list\empty
  824.    \else                           % changes exist
  825.       \ifchbarRight
  826.         \cb_offset = \hsize
  827.         \advance \cb_offset by \BarDistance
  828.       \else
  829.         \cb_offset = \hoffset  
  830.         \advance \cb_offset by -\BarDistance
  831.       \fi
  832.       \setbox\cb_bars=\hbox to \cb_offset{%
  833.          \hskip\cb_offset
  834.          \vbox to 0pt{\offinterlineskip
  835.             \let\cb_bar=\cb_write_bar  \cb_bar_list
  836.             }%
  837.          \hss
  838.          }%
  839.       \ht\cb_bars=0pt \dp\cb_bars=0pt \box\cb_bars
  840.       \global\let\cb_bar_list=\empty
  841.    \fi
  842. %%% \endprog
  843. %%% \beginchange\chap Cleaning Up.\endchange
  844. %%% We finish the macro file so that garbage (e.g.\ of exchanges
  845. %%% between systems) can come afterwards.
  846. %%% \beginprog
  847. \catcode`\@=\atcode
  848. \catcode`\_=\uscode
  849. \endinput
  850. %%% \endprog
  851. %%% \end{document}
  852.